package com.ipan.kits.log4j;

import java.util.Date;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.Layout;
import org.apache.log4j.Level;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.varia.DenyAllFilter;
import org.apache.log4j.varia.LevelMatchFilter;

/**
 * 错误日志进数据库
 * 
 * 仅对Error、Faial级别的日志做记录，具体的记录方式交给外部处理；
 * 中途出错，仅做默认的控制台输出，丢失也不做处理；
 * 
 * #logj4j配置
 * #ErrorToDbAppender继承MyDefineAppender
 * log4j.appender.errorToDb=xxx.xxx.log4j.ErrorToDbAppender
 * log4j.appender.errorToDb.layout=org.apache.log4j.PatternLayout
 * log4j.appender.errorToDb.layout.ConversionPattern=%n%-d{yyyy-MM-dd HH:mm:ss}%n[%p]-[Thread: %t]-[%C.%M()]: %m%n
 * 
 * @author iPan
 * @date 2023-04-14
 */
public abstract class MyDefineAppender extends AppenderSkeleton {
	
	public MyDefineAppender() {
		super();
		LevelMatchFilter filter1 = new LevelMatchFilter();
		filter1.setLevelToMatch(Level.ERROR.toString());
		LevelMatchFilter filter2 = new LevelMatchFilter();
		filter2.setLevelToMatch(Level.FATAL.toString());
		addFilter(filter1); // 捕获ERROR
		addFilter(filter2); // 捕获FATAL
		addFilter(new DenyAllFilter()); // 过滤不满足条件的日志，中断写入日志；
	}

	@Override
	public void close() { // 关闭并释放资源
		this.closed = true;
	}

	@Override
	public boolean requiresLayout() { // 必须配置布局
		return true;
	}
	
	@Override
	protected void append(LoggingEvent event) { // 满足过滤器后追加日志记录，串型写入；
		try {
			String loggerLevel = event.getLevel().toString(); // 日志等级
			String loggerName = event.getLoggerName(); // 日志名称
			String threadName = event.getThreadName(); // 线程名称
			String className = event.getLocationInformation().getClassName(); // 类名
			String methodName = event.getLocationInformation().getMethodName(); // 方法名
			String lineNumber = event.getLocationInformation().getLineNumber(); // 行数
			String renderedMessage = event.getRenderedMessage(); // 异常名称
			String throwableStrRep = StringUtils.join(event.getThrowableStrRep(), Layout.LINE_SEP); // 异常详细
			Date errorTime = new Date(event.getTimeStamp()); // 异常时间
			DefineErrorMessageLog logObj = new DefineErrorMessageLog();
			logObj.setLoggerLevel(loggerLevel);
			logObj.setLoggerName(loggerName);
			logObj.setThreadName(threadName);
			logObj.setClassName(className);
			logObj.setMethodName(methodName);
			logObj.setLineNumber(lineNumber);
			logObj.setRenderedMessage(renderedMessage);
			logObj.setThrowableStrRep(throwableStrRep);
			logObj.setErrorTime(errorTime);
			writeLog(logObj);
		} catch (Exception e) {
			LogLog.error("错误日志入库异常", e); // 默认在控制台输出，不会入库；
		}
	}
	
	/**
	 * 具体写入日志操作
	 * 这块写入操作不需要串行（同步）写入，异步写入也没问题，因为异常的时间是确定的；
	 */
	abstract public void writeLog(DefineErrorMessageLog logObj); 
	
}
